用于在three.js中实现后期处理效果。该类管理了产生最终视觉效果的后期处理过程链。 后期处理过程根据它们添加/插入的顺序来执行,最后一个过程注1会被自动渲染到屏幕上。

EffectComposer( renderer : WebGLRenderer, renderTarget : WebGLRenderTarget )

renderer -- 用于渲染场景的渲染器。 renderTarget -- (可选)一个预先配置的渲染目标,内部由 EffectComposer 使用。

一 概述

一个用于处理后期效果的工具,可以通过一系列Pass(处理单元)实现各种复杂的后期效果,如模糊、景深、轮廓线、抗锯齿等

EffectComposer 是一个附加组件,必须显式导入

javascript
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';

其基本工作流程分为三步:

  1. 创建EffectComposer实例
  2. 配置EffectComposer实例,添加渲染目标注2及一系列(有序的)Pass
  3. 在渲染循环中调用EffectComposer实例的render方法进行渲染,取代webGLRenderer注3

二 实现

1 导入

javascript
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js"; // 渲染场景
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js"; // 描边
import { OutputPass } from "three/addons/postprocessing/OutputPass.js"; // 解决整体变暗bug
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; // 自定义通道
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js"; // 抗锯齿

2 初始化

javascript
let composer, outlinePass, effectFXAA;
const mouse = new THREE.Vector2();
const raycaster = new THREE.Raycaster(); // 射线
let selectedObjects = []; //

function initComposer() {
  composer = new EffectComposer(renderer);

  const renderPass = new RenderPass(scene, camera);
  composer.addPass(renderPass);

  outlinePass = new OutlinePass(
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    scene,
    camera
  );
  outlinePass.visibleEdgeColor.set("#f71b1b"); // 可见部分的描边颜色
  outlinePass.hiddenEdgeColor.set("#ac6b20"); // 不可见部分的描边颜色
  composer.addPass(outlinePass);

  // 到这里发现整体变暗bug,添加OutputPass以解决
  const outputPass = new OutputPass();
  composer.addPass(outputPass);

  // 抗锯齿
  effectFXAA = new ShaderPass(FXAAShader);
  effectFXAA.uniforms["resolution"].value.set(
    1 / window.innerWidth,
    1 / window.innerHeight
  );
  composer.addPass(effectFXAA);
}

3 判断点击

javascript
function checkIntersection(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // 设置射线起点为鼠标位置,射线的方向为相机视角方向
  raycaster.setFromCamera(mouse, camera);
  // 计算射线相交
  const intersects = raycaster.intersectObject(scene, true);

  if (intersects.length > 0) {
    outlinePass.selectedObjects = [intersects[0].object];
  } else {
    outlinePass.selectedObjects = [];
  }
}

4 调用

javascript
onMounted(() => {
  initComposer();
  addEventListener("dblclick", checkIntersection);

  function animate() {
    requestAnimationFrame(animate);
    // renderer.render(scene, camera);
    composer.render();
  }

  animate();
});

三 备注

注1

所谓“最后一个过程”,即是Pass链上的最后一次通道。最后一次通道不会绘制在一个渲染目标上,因为我们可以直接将其放在画布上,这样用户就可以看到最后的结果了

注2

“渲染目标”是Three.js特定的名词,其他地方通常会称其为缓冲区

我们并不是直接在画布上进行渲染,而是在我们称之为"渲染目标"的地方进行渲染。这个渲染目标将会给我们一个非常类似于常规纹理的纹理。说得更简单一点,我们是在一个纹理上进行渲染,而不是在屏幕上的画布上。(也就是webgl中离屏渲染的概念,一个简单的例子是将离屏渲染的东西作为纹理添加到物体上,最后渲染到画布中)

然后,这个纹理被应用到一个面对摄像头并覆盖整个视图的平面上(画布)。这个平面使用了一个特殊的片元着色器的材质,该着色器将进行后期处理效果。如果后期处理效果只是将图像变红,那么它只需在那个片元着色器中将像素的红色值乘以一个值(通常不只只是调整颜色值这么简单)。最后显示到画布上。

在three.js中,这些后处理效果(effects),被称为通道(passes)

注3

javascript
function animate() {
  requestAnimationFrame(animate);
  // renderer.render(scene, camera); // webGLRenderer
  composer.render(); // EffectComposer的render方法
}

调用了EffectComposer的render方法后不需再调用webGLRenderer的render

参考文档

Three.js 如何使用后期处理(How to use post-processing)

【Three.js】知识梳理二十:Three.js后处理EffectComposer

学习Three.js——后期处理(EffectComposer)

three.js后处理